/*!
    \copyright  Copyright (c) 2020 - 2024 Qualcomm Technologies International, Ltd.\n
                All Rights Reserved.\n
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file
    \ingroup    handset_service
    \brief      Handset service connectable
*/

#include "handset_service_advertising.h"
#include "handset_service_connectable.h"
#include "handset_service_protected.h"

#include "qualcomm_connection_manager.h"
#include <bredr_scan_manager.h>
#include <bt_device.h>


static bool allow_bredr;
static bool observing_connections;

static handset_service_connectable_t bredr_state;

static void handsetService_CancelFastPageScanTimeout(void)
{
    MessageCancelFirst(HandsetService_GetTask(), HANDSET_SERVICE_INTERNAL_FAST_PAGE_SCAN_TIMEOUT_IND);
}

static void handsetService_SendFastPageScanTimeout(void)
{
    handsetService_CancelFastPageScanTimeout();
    MessageSendLater(HandsetService_GetTask(), HANDSET_SERVICE_INTERNAL_FAST_PAGE_SCAN_TIMEOUT_IND, NULL, HANDSET_SERVICE_FAST_PAGE_SCAN_TIMEOUT_MS);
}

/*! \brief Check if we should enable truncated page scan or not */
static bool handsetService_IsTruncatedPageScanRequired(void)
{
    /* Enable truncated page scan if feature is enabled and either of below condition is met
       a) Maximum BREDR connections are reached.
       b) Maximum handset connections are reached if LE Barge-in is enabled.
    */

    return HandsetService_IsConnectionBargeInEnabled() &&
#ifdef ENABLE_LE_CONNECTION_BARGE_IN
       HandsetServiceSm_IsMaxHandsetOrMaxBredrAclLimitReached();
#else
       HandsetServiceSm_MaxBredrAclConnectionsReached();
#endif /* ENABLE_LE_CONNECTION_BARGE_IN */
}

static inline handset_service_connectable_t handsetService_CalculateBrEdrConnectableState(void)
{
    unsigned num_bredr_connections = HandsetServiceSm_GetBredrAclConnectionCount();
    bool is_max_connection_reached;

    is_max_connection_reached = num_bredr_connections >= handsetService_BredrAclMaxConnections();

    handset_service_connectable_t setting = handset_service_connectable_disable;

    if (!is_max_connection_reached)
    {
        /* Continue slow page scan if we do not have max connections */
        if(num_bredr_connections)
        {
            setting = handset_service_connectable_enable_slow;
        }
        else
        {
            setting = handset_service_connectable_enable_fast;
        }
    }
    else
    {
        setting = handset_service_connectable_disable;
    }

    HS_LOG("handsetService_CalculateBrEdrConnectableState - %d max_conn_reached %d connected handsets: enum:handset_service_connectable_t:%d",
           num_bredr_connections, is_max_connection_reached, setting);

    return setting;
}

static void handsetService_UpdateBrEdrConnectable(void)
{
    handset_service_connectable_t connectable_state = handsetService_CalculateBrEdrConnectableState();

    if (handsetService_IsTruncatedPageScanRequired())
    {
        if (BredrScanManager_IsTruncatedPageScanEnabledForClient(HandsetService_GetTask()))
        {
            /* Truncated page scan is already enabled and it needs to. So we should not enable normal
               page scan. So override the connectable_state for normal page scan. */
            connectable_state = handset_service_connectable_disable;
        }
        else
        {
            /* Truncated page scan is not enabled. Queue in a timer for enabling truncated page scan after guard period */
            HandsetService_RestartBargeInEnableTimer(HANDSET_SERVICE_PROFILE_CONNECTION_GUARD_PERIOD_MS);
        }
    }
    else
    {
        /* Truncated page scan no longer needed. Disable it. */
        HandsetService_DisableTruncatedPageScan();
    }

    handsetService_ConnectableEnableBredr(connectable_state);
}

void handsetService_ConnectableInit(void)
{
    allow_bredr = FALSE;
    bredr_state = handset_service_connectable_disable;
    observing_connections = FALSE;

    /* Register for connection notifications if peer setup is complete,
       or is not needed. */

    /* In the case of TWM speakers/headsets, they can also behave as standalone speaker/headset. For that reason, peer-pair setup need not be mandatory. */
#ifndef ENABLE_TWM_STEREO
    if (BtDevice_IsPeerSetupComplete())
#endif /*ENABLE_TWM_STEREO*/
    {
        HandsetService_ObserveConnections();
    }
}

void HandsetService_ObserveConnections(void)
{
    if(!observing_connections)
    {
        ConManagerRegisterTpConnectionsObserver(cm_transport_all, HandsetService_GetTask());
        observing_connections = TRUE;
    }
}

void HandsetService_DontObserveConnections(void)
{
    if(observing_connections)
    {
        ConManagerUnregisterTpConnectionsObserver(cm_transport_all, HandsetService_GetTask());
        observing_connections = FALSE;
    }
}

void handsetService_ConnectableEnableBredr(handset_service_connectable_t setting)
{
    if(bredr_state == setting)
    {
        return;
    }
    
    if(!allow_bredr && setting != handset_service_connectable_disable)
    {
        return;
    }
    
    switch(setting)
    {
        case handset_service_connectable_enable_fast:
            BredrScanManager_PageScanRequest(HandsetService_GetTask(), SCAN_MAN_PARAMS_TYPE_FAST);
            handsetService_SendFastPageScanTimeout();
        break;
        
        case handset_service_connectable_enable_slow:
            BredrScanManager_PageScanRequest(HandsetService_GetTask(), SCAN_MAN_PARAMS_TYPE_SLOW);
        break;
        
        case handset_service_connectable_disable:
            BredrScanManager_PageScanRelease(HandsetService_GetTask());
            handsetService_CancelFastPageScanTimeout();
        break;
    }
    
    bredr_state = setting;
}

void handsetService_ConnectableAllowBredr(bool allow)
{
    allow_bredr = allow;
}

void HandsetService_HandleEarbudCreated(void)
{
    HandsetService_ObserveConnections();
}

void handsetService_HandleFastPageScanTimeout(void)
{
    if(allow_bredr && bredr_state != handset_service_connectable_disable)
    {
        handsetService_ConnectableEnableBredr(handset_service_connectable_enable_slow);
    }
}

void HandsetService_EnableTruncatedPageScan(void)
{
    if (allow_bredr)
    {
#ifdef ENABLE_LE_CONNECTION_BARGE_IN
        /* Clear barge in address if it is not already cleared. */
        BdaddrTpSetEmpty(&HandsetService_Get()->barge_in_address);

        /* Disable normal page scan before enabling truncated */
        handsetService_ConnectableEnableBredr(handset_service_connectable_disable);
#endif /* ENABLE_LE_CONNECTION_BARGE_IN */

        HS_LOG("HandsetService_EnableTruncatedPageScan - request truncated page scan");
        BredrScanManager_TruncatedPageScanRequest(HandsetService_GetTask(), SCAN_MAN_PARAMS_TYPE_SLOW);
    }
    else
    {
        HS_LOG("HandsetService_EnableTruncatedPageScan - BR/EDR disabled");
    }
}

void HandsetService_DisableTruncatedPageScan(void)
{
    HS_LOG("HandsetService_DisableTruncatedPageScan");
    MessageCancelAll(HandsetService_GetTask(), HANDSET_SERVICE_INTERNAL_TRUNC_PAGE_SCAN_ENABLE);
    BredrScanManager_TruncatedPageScanRelease(HandsetService_GetTask());
}

void HandsetService_HandleInternalTruncatedPageScanEnable(void)
{
#ifdef ENABLE_LE_CONNECTION_BARGE_IN
    if (HandsetServiceSm_IsMaxHandsetOrMaxBredrAclLimitReached())
#else
    if (HandsetServiceSm_MaxBredrAclConnectionsReached())
#endif /* ENABLE_LE_CONNECTION_BARGE_IN */
    {
        HandsetService_EnableTruncatedPageScan();
    }
}

void handsetService_UpdateConnectable(void)
{
    handsetService_UpdateBrEdrConnectable();

#ifdef ENABLE_LE_CONNECTION_BARGE_IN
    /* Accept LE barge in connection in case if any pending barge in is there to be accepted.
     * But ensure that this happens only after the disconnecting handset complete its disconnection
     * to ensure we don't cross the max handset connections limit.
     * Do not update the advertising here in case of LE barge in pending as we are expecting another connection
     * indication soon.
     */
    if (!BdaddrTpIsEmpty(&HandsetService_Get()->barge_in_address) &&
        HandsetService_Get()->barge_in_address.transport == TRANSPORT_BLE_ACL)
    {
        if (!HandsetService_IsMaxHandsetConnectionLimitReached())
        {
            QcomConManagerAcceptLeBargeInConnection(&HandsetService_Get()->barge_in_address.taddr);
        }
        else
        {
            /* Expected to reach here if a disconnection is in progress. It should not otherwise. */
            HS_LOG("handsetService_UpdateConnectable waiting for disconnect to complete");
        }
    }
    else
#endif /* ENABLE_LE_CONNECTION_BARGE_IN */
    {
#ifndef INCLUDE_LEGACY_LE_ADVERTISING_MANAGER
        HandsetService_UpdateAdvertising();
#endif
    }
}
